package gene;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.lang.Object;

import util.Log;
import base.Manager;
import base.Parameter;
import base.ReferenceParameter;
import factory.Schedule;
import gene.Gene;
import geneComponent.Node;


public class GeneManager extends Manager implements Gene.OnCompleteListenner {
	
	
	public GeneManager() {
		
	}
	
	
	private OnCompleteListenner onCompleteListenner;
	
	public void setOnCompleteListenner(OnCompleteListenner onCompleteListenner) {
		this.onCompleteListenner = onCompleteListenner;
	}

	List<Gene> genePopulation = new ArrayList<Gene>();
	List<Gene> bestGeneList = new ArrayList<Gene>();
	
	
	@Override
	public void init(Parameter parameters, List<ReferenceParameter> rpList) {

		int geneNumber = parameters.getValueToInt(Parameter.TYPE_GENE_POPULATION);

		for (int i = 0; i < geneNumber; i++) {
			Gene gene = new Gene(parameters);
			gene.setOnCompleteListenner(this);
			gene.setName("gene " + i);
			genePopulation.add(gene);
		}
	}
	
	public void run(Parameter parameters, List<ReferenceParameter> rpList){
		for(Gene gene : genePopulation){
			gene.makeAndRunSchedule(parameters.getValueToInt(Parameter.TYPE_REFERENCE_SIZE), parameters, rpList);
		}
	}
	

	@Override
	public void getResult() {

	}

	public Gene getBestGene() {

		double minAverageFitness = Double.MAX_VALUE;
		Gene temp = null;

		for (Gene geneC : genePopulation) {

			if (minAverageFitness > geneC.getAverageFiness()) {
				temp = geneC;
				minAverageFitness = geneC.getAverageFiness();
			}

		}

		return temp;
	}

	public interface OnCompleteListenner {
		/**
		 * @param
		 */
		public abstract void onComplete(GeneManager geneManager);
	}

	@Override
	public void onComplete(Gene gene) {

		if (onCompleteListenner == null)
			return;
		onCompleteListenner.onComplete(this);
	}

	public List<Gene> getGenePopulation() {
		return genePopulation;
	}

	public void setGenePopulation(List<Gene> genePopulation) {
		this.genePopulation = genePopulation;
	}
	
	public List<Gene> generation(Parameter parameters, List<ReferenceParameter> rpList) throws CloneNotSupportedException{
		
//		for(int i=0; i<parameters.getValueToInt(Parameter.TYPE_GENERATION);i++){
//			Gene dummyGene = new Gene(parameters);
//			bestGeneList.add(dummyGene);
//		}
		
		int count=0;
	Log.debug("초기화 ");	
		init(parameters, rpList); //초기화 
		
		while(count<parameters.getValueToInt(Parameter.TYPE_GENERATION)){
	Log.debug("==============================Generation start" + count);		
			run(parameters, rpList); // 실행 
			
			
			for(int i=0; i<genePopulation.size();i++){
				System.out.println(genePopulation.get(i).getName() +"  평균 피트니스 "+getGenePopulation().get(i).getAverageFiness());
			}
			
			bestGeneList.add(copy(getBestGene()));
			
			System.out.println("==============================Genertion " + count + " 베스트 진  "+ getBestGene().getName()+" 평균 에버리지 " + getBestGene().getAverageFiness() + "  피트니스 "+	getBestGene().getScheduleList().get(0).getFitness() +"  "+getBestGene().getScheduleList().get(1).getFitness());
			
			Log.debug("root node " + 	getBestGene().getRootNode().getName());

			if (	getBestGene().getRootNode().getBranches() != null) {
				for (int j = 0; j < 	getBestGene().getRootNode().getArity(); j++) {
					
					Log.debug("root node First Arity "+ j +"  "+ 	getBestGene().getRootNode().getChild(j).getName());

					if (	getBestGene().getRootNode().getChild(j).getBranches() != null) {
						for (int k = 0; k < 	getBestGene().getRootNode().getChild(j).getArity(); k++) {
							Log.debug("root node second Arity " + k +"  " + 	getBestGene().getRootNode().getChild(j).getChild(k).getName());

							if (	getBestGene().getRootNode().getChild(j).getChild(k).getBranches() != null) {
								for (int l = 0; l < 	getBestGene().getRootNode().getChild(j).getChild(k).getArity(); l++) {
									Log.debug("root node third Arity " + l + "   " + 	getBestGene().getRootNode().getChild(j).getChild(k).getChild(l).getName());

									if (	getBestGene().getRootNode().getChild(j).getChild(k).getChild(l).getBranches() != null) {
										for (int m = 0; m < 	getBestGene().getRootNode().getChild(j).getChild(k).getChild(l).getArity(); m++) {
											Log.debug("root node forth Arity " + m +"     "+ 	getBestGene().getRootNode().getChild(j).getChild(k).getChild(l).getChild(m).getName());

											if (	getBestGene().getRootNode().getChild(j).getChild(k).getChild(l).getChild(m).getBranches() != null)
												for (int n = 0; n < 	getBestGene().getRootNode().getChild(j).getChild(k).getChild(l).getChild(m).getArity(); n++) {
													Log.debug("root node forth Arity " + n +"    "
															+ 	getBestGene().getRootNode().getChild(j).getChild(k).getChild(l).getChild(m).getChild(n).getName());
												}
										}
									}
								}
							}
						}
					}

				}
			}
			
			for(int i=0; i<genePopulation.size();i++){
				genePopulation.get(i).scheduleList.clear();
			}
			
			geneMutation(parameters, this.genePopulation);
				
			geneCrossover(parameters, this.genePopulation);
					
			geneReproduction(parameters, this.genePopulation);
			
			
			
	Log.debug("Generation end ");		
			count++;
		}
		
		return bestGeneList;
	}
	
	public List<Gene> geneMutation(Parameter parameters, List<Gene> genePopulation){
		
		Collections.shuffle(genePopulation); //섞음 
		
		double mutationRate = parameters.getValueToDouble(Parameter.TYPE_MUTATION_RATE); //뮤테이션 레이트 불러옴 
		
		for (int i = 0; i < genePopulation.size()*mutationRate; i++) { //뮤테이션 레이트 만큼 뮤테이션 함  
			Gene gene = new Gene(parameters);
			geneMutationCalculation(genePopulation.get(i),gene);
		}
		return genePopulation;
	}
	
	public List<Gene> geneCrossover(Parameter parameters, List<Gene> genePopulation){

		Collections.shuffle(genePopulation); 
		
		double corssoverRate = parameters.getValueToDouble(Parameter.TYPE_CROSSOVER_RATE);
		
		for(int i=0; i<genePopulation.size()*corssoverRate;i+=2){
			geneCrossoverCalculation(genePopulation.get(i),genePopulation.get(i+1));
		}
		return genePopulation;
	}
	
	public List<Gene> geneReproduction(Parameter parameters, List<Gene> genePopulation){
		
		Collections.shuffle(genePopulation);
		
		double reproductionRate = parameters.getValueToDouble(Parameter.TYPE_REPRODUCTION_RATE);
		
		for (int i = 0; i < genePopulation.size()*reproductionRate; i++) { //  
			Gene gene = new Gene(parameters);
			gene.setName("gene(new) " + i);
			genePopulation.set(i, gene);
		}
		
		return genePopulation;
	}
	
	public void geneMutationCalculation(Gene gene, Gene newGene) {

		if (new Random().nextInt(10) > 5) {
			gene.getRootNode().setNode(0, newGene.getRootNode());
		} else {
			gene.getRootNode().setNode(1, newGene.getRootNode());
		}
	}
	
	public void geneCrossoverCalculation(Gene gene1, Gene gene2) {
		
		Node temp1 ;
		Node temp2 ;
		
		if (new Random().nextInt(10) > 5) {
			temp1 = gene1.getRootNode().getNode(0); // gene1의 첫번째 자식 1번을 temp1에 넣음
			temp2 = gene2.getRootNode().getNode(1); // gene2의 첫번째 자식 2번을 temp2에 넣
			
			gene1.getRootNode().setNode(0, temp2);  // gene1의 첫번째 자식 1번에다가 temp2을 넣음 
			gene2.getRootNode().setNode(1, temp1);  // gene2의 첫번째 자식 2번에다가 temp1을 넣음 
		
		} else {
			temp1 = gene1.getRootNode().getNode(1); // gene1의 첫번째 자식 1번을 temp1에 넣음
			temp2 = gene2.getRootNode().getNode(0); // gene2의 첫번째 자식 2번을 temp2에 넣
			
			gene1.getRootNode().setNode(1, temp2);  // gene1의 첫번째 자식 1번에다가 temp2을 넣음 
			gene2.getRootNode().setNode(0, temp1);  // gene2의 첫번째 자식 2번에다가 temp1을 넣음 
		}
	}
	
	public String getBestGeneName(){
		
		return getBestGene().getName();
	}

	public List<Gene> getBestGeneList() {
		return bestGeneList;
	}
	
	public Gene copy(Gene gene) throws CloneNotSupportedException{
		
		Gene returnDummy=null;
		// 더미 진을 선언 
		returnDummy=(Gene) gene.clone();
		// 받아온 진을 복제하여 더미에 넣음 
		returnDummy.scheduleList=(List<Schedule>) ((ArrayList) gene.getScheduleList()).clone();
		return returnDummy;
		
	}
}
